import sys
from typing import TypeVar, Generic, List

from sqlalchemy_tool.model import Model
from sqlalchemy_tool.model_utils import to_dict

ModelType = TypeVar('ModelType', bound=Model)

py_version = sys.version_info
if py_version >= (3, 8):
    from typing import get_args


def get_generic_type_arg(cls):
    t = cls.__orig_bases__[0]
    if py_version >= (3, 8):
        return get_args(t)[0]
    else:
        return t.__args__[0]


class BaseCrdu(Generic[ModelType]):
    """CRDU操作基类：
        方法一：继承该类，如：
            class CategoryInfoCrdu(BaseCrdu[CategoryInfo]): pass
        方法二：直接使用BaseCrdu类，通过[]标注Model类型，如：BaseCrdu[CategoryInfo].list()
    """
    @classmethod
    def get_model_type(cls):
        generic_type = get_generic_type_arg(cls)
        if isinstance(generic_type, TypeVar):
            return cls.model_type
        return generic_type

    def __class_getitem__(cls, item):
        model_type = get_generic_type_arg(cls)
        if issubclass(item, model_type.__bound__):
            cls.model_type = item
        return super().__class_getitem__(item)

    @classmethod
    def add(cls, model: ModelType) -> int:
        """添加数据"""
        model.objects.add()
        return model.id

    @classmethod
    def add_batch(cls, model_list: list[ModelType]) -> bool:
        """批量添加"""
        if model_list:
            return type(model_list[0]).objects_().add_batch(model_list)

    @classmethod
    def save(cls, model: ModelType) -> int:
        """保存"""
        model.objects.save()
        return model.id

    @classmethod
    def save_batch(cls, model_list: list[ModelType]) -> bool:
        """批量保存"""
        if model_list:
            return type(model_list[0]).objects_().save_batch(model_list)

    @classmethod
    def save_batch_by_where(cls, model_list: list[ModelType], criterion_fun=None) -> bool:
        """批量保存（通过criterion_fun接收model判断是否需要更新）"""
        if model_list:
            return type(model_list[0]).objects_().save_batch_by_where(model_list, criterion_fun)

    @classmethod
    def update(cls, model: ModelType, *criterion) -> bool:
        """更新"""
        return model.objects.update(*criterion)

    @classmethod
    def update_batch(cls, model_list: list[ModelType]) -> bool:
        """批量更新"""
        if model_list:
            return type(model_list[0]).objects_().update_batch(model_list)

    @classmethod
    def get_one(cls, *criterion) -> ModelType:
        """根据 where条件查询一条记录"""
        model_type = cls.get_model_type()
        return model_type.objects_().get_one(*criterion)

    @classmethod
    def get_dict(cls, *criterion) -> dict:
        """根据 where条件查询一条记录，转为字典"""
        model_type = cls.get_model_type()
        return to_dict(cls.get_one(model_type, *criterion))

    @classmethod
    def get_by_id(cls, id_val: int) -> ModelType:
        """根据 ID 查询"""
        model_type = cls.get_model_type()
        return model_type.objects_().get_by_id(id_val)

    @classmethod
    def list(cls, *criterion) -> List[ModelType]:
        """通过条件查询，默认查询所有"""
        model_type = cls.get_model_type()
        return model_type.objects_().list(*criterion)

    @classmethod
    def list_by_ids(cls, id_list: List[int]) -> List[ModelType]:
        """通过id集合查询"""
        model_type = cls.get_model_type()
        return model_type.objects_().list_by_ids(id_list)

    @classmethod
    def page(cls, page_num, page_size, *criterion) -> List[ModelType]:
        """分页查询"""
        model_type = cls.get_model_type()
        return model_type.objects_().page(page_num, page_size, *criterion)

    @classmethod
    def count(cls, *criterion) -> int:
        """查询记录数"""
        model_type = cls.get_model_type()
        return model_type.objects_().count(*criterion)

    @classmethod
    def delete(cls, model: ModelType, *criterion) -> bool:
        """
        删除记录
        :param model:
        :param criterion: 删除条件如：CategoryInfo.id <= 280， 默认按照id删除
        """
        return model.objects.delete(*criterion)

    @classmethod
    def delete_by_ids(cls, ids: List[int]) -> bool:
        """根据id批量删除"""
        model_type = cls.get_model_type()
        return model_type.objects_().delete_by_ids(ids)
